#!/bin/bash

# Docker 项目自动部署脚本 - 基于 Git 标签触发
# 作者: Auto Generated
# 日期: $(date +%Y-%m-%d)
# 描述: 通过推送特定标签触发 Docker 项目在测试环境的自动部署

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 配置变量
DEPLOY_TAG_PREFIX="deploy-"
TEST_TAG_PREFIX="test-"
PROD_TAG_PREFIX="prod-"
DOCKER_COMPOSE_FILE="docker-compose.yml"
BACKUP_DIR="/opt/docker-backups"
LOG_DIR="/var/log/docker-deploy"
WEBHOOK_PORT="8080"
DEPLOY_USER="deploy"

# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_DIR/deploy.log"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_DIR/deploy.log"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_DIR/deploy.log"
}

log_step() {
    echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_DIR/deploy.log"
}

# 检查是否为root用户
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "此脚本需要root权限运行"
        exit 1
    fi
}

# 创建必要目录
create_directories() {
    log_step "创建必要目录..."
    
    mkdir -p "$BACKUP_DIR"
    mkdir -p "$LOG_DIR"
    mkdir -p "/opt/docker-projects"
    mkdir -p "/opt/webhook-listener"
    
    chown -R "$DEPLOY_USER:$DEPLOY_USER" "$BACKUP_DIR"
    chown -R "$DEPLOY_USER:$DEPLOY_USER" "$LOG_DIR"
    chown -R "$DEPLOY_USER:$DEPLOY_USER" "/opt/docker-projects"
    chown -R "$DEPLOY_USER:$DEPLOY_USER" "/opt/webhook-listener"
    
    log_info "目录创建完成"
}

# 创建部署用户
create_deploy_user() {
    log_step "创建部署用户..."
    
    if ! id "$DEPLOY_USER" &>/dev/null; then
        useradd --system --shell /bin/bash --home-dir "/home/$DEPLOY_USER" --create-home "$DEPLOY_USER"
        usermod -aG docker "$DEPLOY_USER"
        log_info "用户 $DEPLOY_USER 创建成功"
    else
        log_info "用户 $DEPLOY_USER 已存在"
        usermod -aG docker "$DEPLOY_USER"
    fi
}

# 创建 Webhook 监听服务
create_webhook_listener() {
    log_step "创建 Webhook 监听服务..."
    
    cat > /opt/webhook-listener/webhook-server.py << 'EOF'
#!/usr/bin/env python3
import json
import subprocess
import logging
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import os
import sys

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('/var/log/docker-deploy/webhook.log'),
        logging.StreamHandler(sys.stdout)
    ]
)

class WebhookHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        try:
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length)
            
            # 解析 Gitea Webhook 数据
            webhook_data = json.loads(post_data.decode('utf-8'))
            
            # 检查是否为标签推送事件
            if webhook_data.get('ref_type') == 'tag':
                tag_name = webhook_data.get('ref', '').replace('refs/tags/', '')
                repo_name = webhook_data.get('repository', {}).get('name', '')
                repo_url = webhook_data.get('repository', {}).get('clone_url', '')
                
                logging.info(f"收到标签推送事件: {tag_name} for {repo_name}")
                
                # 检查标签前缀并执行相应部署
                if tag_name.startswith('deploy-'):
                    self.handle_deploy(repo_name, repo_url, tag_name, 'test')
                elif tag_name.startswith('test-'):
                    self.handle_deploy(repo_name, repo_url, tag_name, 'test')
                elif tag_name.startswith('prod-'):
                    self.handle_deploy(repo_name, repo_url, tag_name, 'production')
                else:
                    logging.info(f"忽略标签: {tag_name} (不匹配部署前缀)")
            
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(json.dumps({"status": "ok"}).encode())
            
        except Exception as e:
            logging.error(f"处理 Webhook 时出错: {str(e)}")
            self.send_response(500)
            self.end_headers()
    
    def handle_deploy(self, repo_name, repo_url, tag_name, environment):
        """处理部署请求"""
        try:
            logging.info(f"开始部署 {repo_name} 标签 {tag_name} 到 {environment} 环境")
            
            # 调用部署脚本
            deploy_script = "/opt/webhook-listener/deploy-project.sh"
            cmd = [deploy_script, repo_name, repo_url, tag_name, environment]
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            
            if result.returncode == 0:
                logging.info(f"部署成功: {repo_name} {tag_name}")
            else:
                logging.error(f"部署失败: {result.stderr}")
                
        except subprocess.TimeoutExpired:
            logging.error(f"部署超时: {repo_name} {tag_name}")
        except Exception as e:
            logging.error(f"部署过程中出错: {str(e)}")
    
    def log_message(self, format, *args):
        # 重写日志方法，使用我们的日志配置
        logging.info(f"{self.address_string()} - {format % args}")

def run_server(port=8080):
    server_address = ('', port)
    httpd = HTTPServer(server_address, WebhookHandler)
    logging.info(f"Webhook 服务器启动在端口 {port}")
    httpd.serve_forever()

if __name__ == '__main__':
    port = int(os.environ.get('WEBHOOK_PORT', 8080))
    run_server(port)
EOF

    chmod +x /opt/webhook-listener/webhook-server.py
    
    log_info "Webhook 监听服务创建完成"
}

# 创建项目部署脚本
create_deploy_script() {
    log_step "创建项目部署脚本..."
    
    cat > /opt/webhook-listener/deploy-project.sh << 'EOF'
#!/bin/bash

# 项目部署脚本
# 参数: $1=项目名 $2=仓库URL $3=标签名 $4=环境

set -e

REPO_NAME="$1"
REPO_URL="$2"
TAG_NAME="$3"
ENVIRONMENT="$4"

PROJECT_DIR="/opt/docker-projects/$REPO_NAME"
BACKUP_DIR="/opt/docker-backups"
LOG_FILE="/var/log/docker-deploy/deploy-$REPO_NAME.log"

# 日志函数
log_info() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_FILE"
}

log_error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_FILE"
}

log_info "开始部署项目: $REPO_NAME, 标签: $TAG_NAME, 环境: $ENVIRONMENT"

# 创建项目目录
mkdir -p "$PROJECT_DIR"
cd "$PROJECT_DIR"

# 备份当前运行的容器（如果存在）
if [ -f "docker-compose.yml" ] && docker-compose ps | grep -q "Up"; then
    log_info "备份当前运行的容器..."
    BACKUP_NAME="$REPO_NAME-$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
    
    # 导出容器数据
    docker-compose ps -q | while read container_id; do
        if [ ! -z "$container_id" ]; then
            docker export "$container_id" > "$BACKUP_DIR/$BACKUP_NAME/container-$container_id.tar"
        fi
    done
    
    # 停止当前容器
    docker-compose down
    log_info "容器已停止并备份到: $BACKUP_DIR/$BACKUP_NAME"
fi

# 克隆或更新代码
if [ -d ".git" ]; then
    log_info "更新现有仓库..."
    git fetch --all --tags
    git checkout "$TAG_NAME"
    git pull origin "$TAG_NAME" || true
else
    log_info "克隆新仓库..."
    git clone "$REPO_URL" .
    git checkout "$TAG_NAME"
fi

# 检查 Docker Compose 文件
if [ ! -f "docker-compose.yml" ] && [ ! -f "docker-compose.yaml" ]; then
    log_error "未找到 docker-compose.yml 文件"
    exit 1
fi

# 根据环境设置不同的配置
case "$ENVIRONMENT" in
    "test")
        if [ -f "docker-compose.test.yml" ]; then
            COMPOSE_FILE="docker-compose.yml:docker-compose.test.yml"
        else
            COMPOSE_FILE="docker-compose.yml"
        fi
        ;;
    "production")
        if [ -f "docker-compose.prod.yml" ]; then
            COMPOSE_FILE="docker-compose.yml:docker-compose.prod.yml"
        else
            COMPOSE_FILE="docker-compose.yml"
        fi
        ;;
    *)
        COMPOSE_FILE="docker-compose.yml"
        ;;
esac

# 设置环境变量
export COMPOSE_FILE="$COMPOSE_FILE"
export DEPLOY_TAG="$TAG_NAME"
export DEPLOY_ENV="$ENVIRONMENT"

# 构建和启动容器
log_info "开始构建和启动容器..."
docker-compose pull || true
docker-compose build --no-cache
docker-compose up -d

# 等待容器启动
sleep 10

# 检查容器状态
if docker-compose ps | grep -q "Up"; then
    log_info "部署成功! 容器已启动"
    
    # 显示运行状态
    docker-compose ps | tee -a "$LOG_FILE"
    
    # 清理旧镜像
    docker image prune -f
    
else
    log_error "部署失败! 容器未能正常启动"
    docker-compose logs | tee -a "$LOG_FILE"
    exit 1
fi

log_info "项目 $REPO_NAME 部署完成"
EOF

    chmod +x /opt/webhook-listener/deploy-project.sh
    chown "$DEPLOY_USER:$DEPLOY_USER" /opt/webhook-listener/deploy-project.sh
    
    log_info "项目部署脚本创建完成"
}

# 创建系统服务
create_systemd_service() {
    log_step "创建 systemd 服务..."
    
    cat > /etc/systemd/system/docker-webhook.service << EOF
[Unit]
Description=Docker Webhook Deployment Service
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
User=$DEPLOY_USER
Group=$DEPLOY_USER
WorkingDirectory=/opt/webhook-listener
Environment=WEBHOOK_PORT=$WEBHOOK_PORT
ExecStart=/usr/bin/python3 /opt/webhook-listener/webhook-server.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable docker-webhook
    
    log_info "systemd 服务创建完成"
}

# 配置防火墙
configure_firewall() {
    log_step "配置防火墙..."
    
    if systemctl is-active --quiet firewalld; then
        firewall-cmd --permanent --add-port=$WEBHOOK_PORT/tcp
        firewall-cmd --reload
        log_info "防火墙配置完成"
    else
        log_warn "firewalld 未运行，跳过防火墙配置"
    fi
}

# 启动服务
start_service() {
    log_step "启动 Webhook 服务..."
    
    systemctl start docker-webhook
    
    if systemctl is-active --quiet docker-webhook; then
        log_info "Webhook 服务启动成功"
    else
        log_error "Webhook 服务启动失败"
        systemctl status docker-webhook
        exit 1
    fi
}

# 创建使用说明
create_usage_guide() {
    log_step "创建使用说明..."
    
    cat > /opt/webhook-listener/README.md << 'EOF'
# Docker 自动部署系统使用说明

## 触发部署的方法

### 1. 通过 Git 标签触发部署

在项目中推送特定前缀的标签来触发不同环境的部署：

```bash
# 触发测试环境部署
git tag deploy-v1.0.0
git push origin deploy-v1.0.0

# 或者使用 test- 前缀
git tag test-v1.0.0  
git push origin test-v1.0.0

# 触发生产环境部署
git tag prod-v1.0.0
git push origin prod-v1.0.0
```

### 2. 标签命名规则

- `deploy-*`: 部署到测试环境
- `test-*`: 部署到测试环境  
- `prod-*`: 部署到生产环境

### 3. 项目要求

项目根目录必须包含：
- `docker-compose.yml` (必需)
- `docker-compose.test.yml` (可选，测试环境配置)
- `docker-compose.prod.yml` (可选，生产环境配置)

### 4. 在 Gitea 中配置 Webhook

1. 进入项目设置 -> Webhooks
2. 添加新的 Webhook
3. URL: `http://your-server-ip:8080`
4. 内容类型: `application/json`
5. 触发条件: 选择 "推送" 和 "创建"
6. 激活 Webhook

### 5. 查看部署日志

```bash
# 查看 Webhook 服务日志
sudo journalctl -u docker-webhook -f

# 查看部署日志
sudo tail -f /var/log/docker-deploy/deploy.log

# 查看特定项目部署日志
sudo tail -f /var/log/docker-deploy/deploy-项目名.log
```

### 6. 管理命令

```bash
# 启动服务
sudo systemctl start docker-webhook

# 停止服务  
sudo systemctl stop docker-webhook

# 重启服务
sudo systemctl restart docker-webhook

# 查看服务状态
sudo systemctl status docker-webhook
```

### 7. 手动部署

如果需要手动触发部署：

```bash
sudo -u deploy /opt/webhook-listener/deploy-project.sh 项目名 仓库URL 标签名 环境
```

例如：
```bash
sudo -u deploy /opt/webhook-listener/deploy-project.sh myapp https://gitea.example.com/org/myapp.git deploy-v1.0.0 test
```
EOF

    chown "$DEPLOY_USER:$DEPLOY_USER" /opt/webhook-listener/README.md
    
    log_info "使用说明创建完成"
}

# 显示安装信息
show_info() {
    log_step "安装完成！"
    echo
    log_info "Docker 自动部署系统安装信息:"
    echo "  - Webhook 监听端口: $WEBHOOK_PORT"
    echo "  - 部署用户: $DEPLOY_USER"
    echo "  - 项目目录: /opt/docker-projects"
    echo "  - 备份目录: $BACKUP_DIR"
    echo "  - 日志目录: $LOG_DIR"
    echo "  - 配置目录: /opt/webhook-listener"
    echo
    log_info "在 Gitea 中配置 Webhook:"
    echo "  - URL: http://$(hostname -I | awk '{print $1}'):$WEBHOOK_PORT"
    echo "  - 内容类型: application/json"
    echo "  - 触发条件: 推送 + 创建"
    echo
    log_info "触发部署的标签前缀:"
    echo "  - deploy-* 或 test-*: 部署到测试环境"
    echo "  - prod-*: 部署到生产环境"
    echo
    log_info "常用命令:"
    echo "  - 查看服务状态: systemctl status docker-webhook"
    echo "  - 查看实时日志: journalctl -u docker-webhook -f"
    echo "  - 重启服务: systemctl restart docker-webhook"
    echo
    log_warn "使用说明文档: /opt/webhook-listener/README.md"
}

# 主函数
main() {
    log_info "开始安装 Docker 自动部署系统"
    echo
    
    check_root
    create_deploy_user
    create_directories
    create_webhook_listener
    create_deploy_script
    create_systemd_service
    configure_firewall
    start_service
    create_usage_guide
    show_info
    
    log_info "Docker 自动部署系统安装完成！"
}

# 执行主函数
main "$@"
